#include <stdio.h>
#include <string.h>
#include <sys/time.h> 
#include <stdlib.h>
#include <arm_neon.h>

#ifdef PLATFORM_ANDROID_ARM64
#define TEST_DATA_PATH "/data/local/tmp/hand800.jpg"
#define TEST_OUT_ARM_DATA_PATH "/data/local/tmp/hand800_outArm.jpg"
#define TEST_OUT_NEON_DATA_PATH "/data/local/tmp/hand800_outNEON.jpg"
#elif defined (PLATFORM_LINUX_ARM64)
#define TEST_DATA_PATH "hand800.jpg"
#define TEST_OUT_ARM_DATA_PATH "hand800_outArm.jpg"
#define TEST_OUT_NEON_DATA_PATH "hand800_outNEON.jpg"
#endif

// image size
#define TEST_DATA_LENGTH_BYTES (41287)
// loop 1024 times
#define TEST_TIMES 10240

// time variables
struct timeval start,end;

long timeuse_us;

// read data to buff
void read_data(uint8_t * load_buf,char* load_path,uint32_t load_bytes)
{
    FILE * filePtr = fopen(load_path,"rb");
    fread(load_buf,load_bytes,sizeof(uint8_t),filePtr);
    fclose(filePtr);
}

// save data to file
void save_data(uint8_t * store_buf,char* store_path,uint32_t store_bytes)
{
    FILE * filePtr;
    filePtr=fopen(store_path,"wb");
    fwrite(store_buf,store_bytes,sizeof(uint8_t),filePtr);
    fclose(filePtr);
}


void load_store_no_neon(uint8_t * src, uint8_t *dst, uint32_t size_bytes)
{
    uint32_t i=0;
    for(i=0;i<size_bytes;i++)
    {
        *(dst++) = *(src++);
    }
}
void load_store_neon(uint8_t * src, uint8_t *dst, uint32_t size_bytes)
{
    // for Q register ,128 bits,so it can save 16 u8 datas.
    #define SIMD_LENGTH_BYTES_THIS_FUNCTION_LOG2 (4) //In order To Shift In Binary
    // Make Sure Whether SIZE is aligned with SIMD_LENGTH
    uint32_t simd_loops = size_bytes >> SIMD_LENGTH_BYTES_THIS_FUNCTION_LOG2;
    uint32_t simd_size = simd_loops << SIMD_LENGTH_BYTES_THIS_FUNCTION_LOG2;
    uint32_t non_simd_size = size_bytes-simd_size;
    
    // printf("ori = %d now = %d \n",size_bytes,simd_size);
    // DO SIMD LOOPS
    int i = 0;
    for(i=0;i<size_bytes;i+=16)
    {

        vst1q_u8(dst,vld1q_u8(src));
        dst+=16;
        src+=16;
    
    }
    // DO NON SIMD LOOPS
    if(non_simd_size)  load_store_no_neon(src-16,dst-16,non_simd_size);

    #undef SIMD_LENGTH_BYTES_THIS_FUNCTION_LOG2
}


int main()
{
    uint8_t * load_buf = (uint8_t*) malloc(TEST_DATA_LENGTH_BYTES);
    uint8_t * store_buf_neon = (uint8_t*) malloc(TEST_DATA_LENGTH_BYTES);
    uint8_t * store_buf_arm = (uint8_t*) malloc(TEST_DATA_LENGTH_BYTES);


    read_data(load_buf,TEST_DATA_PATH,TEST_DATA_LENGTH_BYTES);


    gettimeofday(&start,NULL); 
    for(int i=0;i<TEST_TIMES;i++)
    {
        load_store_neon(load_buf,store_buf_neon,TEST_DATA_LENGTH_BYTES);
    }
        
    gettimeofday(&end,NULL); 
    save_data(store_buf_neon,TEST_OUT_NEON_DATA_PATH,TEST_DATA_LENGTH_BYTES);
    timeuse_us=1000000*(end.tv_sec-start.tv_sec) + end.tv_usec-start.tv_usec; 
    printf("############################################################################\n");
    printf("############################################################################\n");  
    printf("Neon copy latency =%ld us; MOVE SIZE = %d times * %d bytes\n",timeuse_us,TEST_TIMES,TEST_DATA_LENGTH_BYTES);
  
    gettimeofday(&start,NULL); 
    for(int i=0;i<TEST_TIMES;i++)
    {
        load_store_no_neon (load_buf,store_buf_arm,TEST_DATA_LENGTH_BYTES);
    }
       
    gettimeofday(&end,NULL); 
    save_data(store_buf_arm,TEST_OUT_ARM_DATA_PATH,TEST_DATA_LENGTH_BYTES);
    timeuse_us=1000000*(end.tv_sec-start.tv_sec) + end.tv_usec-start.tv_usec; 
    printf("No neon copy latency =%ld us; MOVE SIZE = %d times * %d bytes\n",timeuse_us,TEST_TIMES,TEST_DATA_LENGTH_BYTES);
    
    free(load_buf);
    free(store_buf_arm);
    free(store_buf_neon);
    printf("############################################################################\n");
    printf("############################################################################\n");  
    return 0;
}